package httpclient

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"time"
)

// HttpClient 提供带超时和重试机制的HTTP客户端
type HttpClient struct {
	client  *http.Client
	baseURL string
	retries int
}

// Response HTTP响应结构
type Response struct {
	Code int             `json:"code"`
	Msg  string          `json:"msg"`
	Data json.RawMessage `json:"data,omitempty"`
}

// NewHttpClient 创建HTTP客户端
func NewHttpClient(baseURL string, timeout time.Duration, retries int) *HttpClient {
	// 创建带超时控制的HTTP客户端
	client := &http.Client{
		Timeout: timeout,
		Transport: &http.Transport{
			DialContext: (&net.Dialer{
				Timeout:   5 * time.Second,  // 连接超时
				KeepAlive: 30 * time.Second, // 保持连接时间
			}).DialContext,
			MaxIdleConns:        100,              // 最大空闲连接数
			MaxIdleConnsPerHost: 20,               // 每个主机最大空闲连接数
			IdleConnTimeout:     90 * time.Second, // 空闲连接超时
		},
	}

	return &HttpClient{
		client:  client,
		baseURL: baseURL,
		retries: retries,
	}
}

// Get 发送GET请求
func (c *HttpClient) Get(ctx context.Context, path string, params map[string]string) (*Response, error) {
	// 构建URL
	url := c.baseURL + path

	// 构建请求
	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("创建请求失败: %w", err)
	}

	// 添加查询参数
	q := req.URL.Query()
	for k, v := range params {
		q.Add(k, v)
	}
	req.URL.RawQuery = q.Encode()

	// 发送请求并重试
	var resp *http.Response
	var retryErr error
	for i := 0; i <= c.retries; i++ {
		resp, retryErr = c.client.Do(req)
		if retryErr == nil {
			break
		}
		if i < c.retries {
			// 线性退避策略
			time.Sleep(time.Duration(i+1) * 100 * time.Millisecond)
		}
	}
	if retryErr != nil {
		return nil, fmt.Errorf("请求失败: %w", retryErr)
	}
	defer resp.Body.Close()

	// 读取响应内容
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("读取响应失败: %w", err)
	}

	// 解析响应
	var response Response
	if err := json.Unmarshal(body, &response); err != nil {
		return nil, fmt.Errorf("解析响应失败: %w", err)
	}

	return &response, nil
}

// Post 发送POST请求
func (c *HttpClient) Post(ctx context.Context, path string, data interface{}) (*Response, error) {
	// 构建URL
	url := c.baseURL + path

	// 序列化请求数据
	jsonData, err := json.Marshal(data)
	if err != nil {
		return nil, fmt.Errorf("序列化请求数据失败: %w", err)
	}

	// 构建请求
	req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, fmt.Errorf("创建请求失败: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")

	// 发送请求并重试
	var resp *http.Response
	var retryErr error
	for i := 0; i <= c.retries; i++ {
		resp, retryErr = c.client.Do(req)
		if retryErr == nil {
			break
		}
		if i < c.retries {
			// 线性退避策略
			time.Sleep(time.Duration(i+1) * 100 * time.Millisecond)
		}
	}
	if retryErr != nil {
		return nil, fmt.Errorf("请求失败: %w", retryErr)
	}
	defer resp.Body.Close()

	// 读取响应内容
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("读取响应失败: %w", err)
	}

	// 解析响应
	var response Response
	if err := json.Unmarshal(body, &response); err != nil {
		return nil, fmt.Errorf("解析响应失败: %w", err)
	}

	return &response, nil
}
